/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.diagram.activity.helper;

import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.uml2.uml.Element;

import com.cea.papyrus.core.helper.NodeHelper;
import com.cea.papyrus.core.utils.Debug;
import com.cea.papyrus.core.utils.LookForElement;
import com.cea.papyrus.diagraminterchange2.di2.Diagram;
import com.cea.papyrus.diagraminterchange2.di2.GraphElement;
import com.cea.papyrus.diagraminterchange2.di2.GraphNode;
import com.cea.papyrus.diagraminterchange2.di2.Uml1SemanticModelBridge;

/**
 * Manages Pins
 *
 * @author Gilles Cannenterre
 * @since 28 mars 07
 */
public class NodePinHelper extends NodeHelper {

	/** The default container. */
	private GraphElement defaultContainer;

	/**
	 * this method calculate the good position of a pin by taking account of his parent.
	 * 
	 * @param portDimension the dimension of the pin
	 * @param parentGP graphNode the virtual graphNode that contains the pin
	 * @param point the absolute position
	 * 
	 * @return the new position of the pin.
	 */
	public Point inRunner(GraphNode parentGP,Point point, Dimension portDimension){
		int x= point.x;
		int y= point.y;
		Point selected =point.getCopy();
		//calculate absolute position of GP
	
	
		Point positionGP=LookForElement.calculateAbsolutePosition(parentGP,new Point(0,0));
	
	
		// position is constraint in the parent rectangle
		if (x<positionGP.x-portDimension.width){
			x=positionGP.x-portDimension.width;
		}
		if (x>positionGP.x+parentGP.getSize().width){
			x=positionGP.x+parentGP.getSize().width;
		}
		if (y<positionGP.y-portDimension.height){
			y=positionGP.y-portDimension.height;
		}
		if (y>positionGP.y+parentGP.getSize().height){
			y=positionGP.y+parentGP.getSize().height;
		}
	
		//calculte all 4 possible positions.		
		Point pointWEST= new Point(positionGP.x-portDimension.width, y);
		Point pointNORTH= new Point(x,positionGP.y-portDimension.height);
		Point pointEAST= new Point(positionGP.x+parentGP.getSize().width, y);
		Point pointSOUTH= new Point(x,positionGP.y+parentGP.getSize().height);
	
		// calculate all distance
		double distanceWEST=pointWEST.getDistance(point);
		double distanceNORTH=pointNORTH.getDistance(point);
		double distanceEAST=pointEAST.getDistance(point);
		double distanceSOUTH=pointSOUTH.getDistance(point);
		Debug.debug(this,"pointWEST: "+pointWEST+" "+distanceWEST,channel);
		Debug.debug(this,"pointNORTH: "+pointNORTH+" "+distanceNORTH,channel);
		Debug.debug(this,"pointEAST: "+pointEAST+" "+distanceEAST,channel);
		Debug.debug(this,"pointSOUTH: "+pointSOUTH+" "+distanceSOUTH,channel);
	
		//choose the point the minimum distance
		selected=pointWEST;
		double selecteddistance=distanceWEST;
		if (selecteddistance>distanceNORTH){
			selected=pointNORTH;
			selecteddistance=distanceNORTH;}
	
		if (selecteddistance>distanceEAST){
			selected=pointEAST;
			selecteddistance=distanceEAST;}
	
		if (selecteddistance>distanceSOUTH){
			selected=pointSOUTH;
			selecteddistance=distanceSOUTH;}
		Debug.debug(this,"miminmum distance: "+selected+" "+selecteddistance,channel);
		
		return selected;
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.common.helper.NodeHelper#addGraphElement(com.cea.papyrus.core.diagraminterchange2.di2.GraphElement, com.cea.papyrus.core.diagraminterchange2.di2.GraphElement)
	 */
	@Override
	public GraphElement addGraphElement(GraphElement child, GraphElement parent) {
		child.setSemanticParent(parent);
		if (child.getSemanticParent().getContainer() != null) {
			child.getSemanticParent().getContainer().getContained().add(child);
		} else {
			defaultContainer.getContained().add(child);
		}
		return child;
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.common.helper.NodeHelper#removeGraphElement(com.cea.papyrus.core.diagraminterchange2.di2.GraphElement, com.cea.papyrus.core.diagraminterchange2.di2.GraphElement)
	 */
	@Override
	public boolean removeGraphElement(GraphElement child, GraphElement parent){
		// Save the default container for undo command...
		defaultContainer = child.getContainer();
		child.setSemanticParent(null);
		child.eAdapters().clear();
		if (child.getContainer() != null) {
			child.getContainer().getContained().remove(child);
		}
		return true;
	}

	/**
	 * 
	 * 
	 * @param child 
	 * @param parent 
	 * 
	 * @return 
	 */
	public boolean undoCreateGraphElement(GraphElement child, GraphElement parent){
		child.setSemanticParent(null);
		child.eAdapters().clear();
		for(int i=0; i<child.getSemanticChild().size();){
			GraphElement subchild=(GraphElement)child.getSemanticChild().get(i);
			subchild.eAdapters().clear();
			subchild.setSemanticParent(null);
			subchild.getContainer().getContained().remove(subchild);
		}
		 child.getContainer().getContained().remove(child);
		 return true;
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.common.helper.NodeHelper#getGraphNodeSize()
	 */
	@Override
	public Dimension getGraphNodeSize() {
		return new Dimension(15,15);
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.common.helper.NodeHelper#canCreateGraphElement(com.cea.papyrus.core.diagraminterchange2.di2.GraphElement)
	 */
	@Override
	public boolean canCreateGraphElement(GraphElement parent){
		if (parent instanceof Diagram) {
			return false;
		} else if (parent.getSemanticModel()!=null){
			Element parentElt= ((Uml1SemanticModelBridge)parent.getSemanticModel()).getElement();
			if(parentElt!=null){
				return canCreateUMLElement(parentElt);
			}
		}
		return false;
	}

	///////////////////////////////////////////////////////////////////////////
	// GRAPHICAL MANAGEMENT 
	///////////////////////////////////////////////////////////////////////////
	
	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.common.factories.INodeFactory#createGraphNode()
	 */
	/**
	 * 
	 * 
	 * @param umlChild 
	 * @param size 
	 * @param location 
	 * @param parent 
	 * 
	 * @return 
	 */
	@Override
	public GraphNode createGraphElement(Element umlChild, GraphElement parent, Point location, Dimension size){
		defaultContainer = parent.getContainer();
		// graph element must be added to the parent graph element (specific to pin)
		GraphNode node = super.createGraphElement(umlChild, parent.getContainer(), location, size);
		Point newpos= new Point(0,0);
		
		newpos=LookForElement.calculateAbsolutePosition((GraphNode)parent, location);
		//to say that the pin belongs to entity parent
		node.setSemanticParent(parent);
		// set defaut size, if null
		node.setSize(getGraphNodeSize());
	
		// set correct position: calculate position for the pin
		node.setPosition(inRunnerAtCreation((GraphNode)parent, newpos, node.getSize()));
		Point origin=LookForElement.calculateAbsolutePosition(((GraphNode)parent.getContainer()),new Point(0,0));
		node.setPosition(node.getPosition().getTranslated(origin.getNegated().getCopy()));
		
		
		// node.setPosition(newpos);
		//create label
		NodeHelper nh= new NodeHelper();
		GraphElement ge= nh.createGraphElement(null, null, new Point(node.getPosition().x+15, node.getPosition().y), null);
		ge.setSemanticParent(node);
		ge.setContainer(parent.getContainer());
	
		return node;
	}

	/**
	 * this method calculate the good position of a pin by taking account of his parent.
	 * 
	 * @param portDimension the dimension of the pin
	 * @param parentGP graphNode the virtual graphNode that contains the pin
	 * @param point the absolute position
	 * 
	 * @return the new position of the pin.
	 */
	private Point inRunnerAtCreation(GraphNode parentGP,Point point, Dimension portDimension){
		int x= point.x;
		int y= point.y;
		Point selected =point.getCopy();

		Point positionGP = LookForElement.calculateAbsolutePosition(parentGP,new Point(0,0));
	
		// position is constraint in the parent rectangle
		if (x<positionGP.x-portDimension.width){
			x=positionGP.x-portDimension.width;
		}
		if (x>positionGP.x+parentGP.getSize().width){
			x=positionGP.x+parentGP.getSize().width;
		}
		if (y<positionGP.y-portDimension.height){
			y=positionGP.y-portDimension.height;
		}
		if (y>positionGP.y+parentGP.getSize().height){
			y=positionGP.y+parentGP.getSize().height;
		}
	
		//calculte all 4 possible positions.		
		Point pointWEST= new Point(positionGP.x-portDimension.width, y);
		Point pointNORTH= new Point(x,positionGP.y-portDimension.height);
		Point pointEAST= new Point(positionGP.x+parentGP.getSize().width, y);
		Point pointSOUTH= new Point(x,positionGP.y+parentGP.getSize().height);
	
		// calculate all distance
		double distanceWEST=pointWEST.getDistance(point);
		double distanceNORTH=pointNORTH.getDistance(point);
		double distanceEAST=pointEAST.getDistance(point);
		double distanceSOUTH=pointSOUTH.getDistance(point);
		Debug.debug(this,"pointWEST: "+pointWEST+" "+distanceWEST,channel);
		Debug.debug(this,"pointNORTH: "+pointNORTH+" "+distanceNORTH,channel);
		Debug.debug(this,"pointEAST: "+pointEAST+" "+distanceEAST,channel);
		Debug.debug(this,"pointSOUTH: "+pointSOUTH+" "+distanceSOUTH,channel);
	
		//choose the point the minimum distance
		selected=pointWEST;
		double selecteddistance=distanceWEST;
		if (selecteddistance>distanceNORTH){
			selected=pointNORTH;
			selecteddistance=distanceNORTH;}
	
		if (selecteddistance>distanceEAST){
			selected=pointEAST;
			selecteddistance=distanceEAST;}
	
		if (selecteddistance>distanceSOUTH){
			selected=pointSOUTH;
			selecteddistance=distanceSOUTH;}
		Debug.debug(this,"miminmum distance: "+selected+" "+selecteddistance,channel);
		return selected;
	}

}
